package zunge.lbe.analysis;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;

import zunge.tools.Tools;

public class Irlba {
	public static final int N = 100; //#dimensions
	
	public String folder;
	public String filePrefix;
	public HashMap<Long,Integer> mMap;
	
	public static final String fsuf_matrix = "_matrix.txt";
	public static final String fsuf_mMap = "_mMap.txt";
	public static final String fsuf_uf = "_uf.txt";
	public static final String fsuf_mf = "_mf.txt";
	public static final String fsuf_record = "_record.txt";
	public static final String fsuf_prediction = "_prediction.txt";
	public static final String fsuf_log = "_log.txt";
	public static final String fsuf_lltrain = "_lltrain.txt";
	public static final String fsuf_lltest = "_lltest.txt";
	public String getFile(String label){
		return folder+filePrefix+label;
	}
	public Irlba(String folder, String filePrefix){
		this.folder = folder;
		this.filePrefix = filePrefix;
	}
	
	public static int FLAG_UP = 0;
	public static int FLAG_UAP = 1;
	
	/** Input file format: uid aid pid did */
	public void genData(DataSet dataSet, String fin_train, String fin_record, int uCount, int flag) throws Exception{
		mMap = new HashMap<Long, Integer>();
		
		if(flag == FLAG_UP){
			for(int p = 1;p <= DataSet.P;++p){mMap.put((long)p, p);}
			Tools.saveMap(mMap, getFile(fsuf_mMap));
			
			//Generate Matrix
			double[][] acc = new double[uCount][DataSet.P];
			double[][] rej = new double[uCount][DataSet.P];
			for(int u = 0;u < uCount;++u){Arrays.fill(acc[u], 0);Arrays.fill(rej[u], 0);}
			BufferedReader br = new BufferedReader(new FileReader(fin_train));
			String line;
			int uid,pid,did;
			while((line=br.readLine())!=null){
				String[] frags = line.split(" ");
				uid = Integer.parseInt(frags[0]);
				pid = Integer.parseInt(frags[2]);
				did = Integer.parseInt(frags[3]);
				if(did==1){acc[uid-1][pid-1]+=1;}
				else if(did==-1){rej[uid-1][pid-1]+=1;}
			}
			br.close();
			
			//Write Matrix
			BufferedWriter bw = new BufferedWriter(new FileWriter(getFile(fsuf_matrix)));
			for(int u = 0;u < uCount;++u){
				for(int p = 0;p < DataSet.P;++p){
					double ratio = (acc[u][p]+rej[u][p]==0)?0:((acc[u][p]-rej[u][p])/(acc[u][p]+rej[u][p]));
					bw.write((u+1)+" "+(p+1)+" "+ratio+"\n");
				}
			}
			bw.write((uCount+1)+" "+(DataSet.P+1)+" "+0+"\n");//Fake Record
			bw.close();

			//Write Record for regression
			HashSet<Record> records = new HashSet<Record>();
			br = new BufferedReader(new FileReader(fin_record));
			
			while((line=br.readLine())!=null){
				String[] frags = line.split(" ");
				uid = Integer.parseInt(frags[0]);
				pid = Integer.parseInt(frags[2]);
				records.add(new Record(uid, pid));
			}
			br.close();
			bw = new BufferedWriter(new FileWriter(getFile(fsuf_record)));
			for(Record record:records){
				bw.write(record.uid+" "+record.mid+"\n");
			}
			bw.close();
		}
		else if(flag == FLAG_UAP){
			//Generate mMap and matrix
			BufferedReader br = new BufferedReader(new FileReader(fin_train));
			BufferedWriter bw = new BufferedWriter(new FileWriter(getFile(fsuf_matrix)));
			String line;
			long m_id;int uid,aid,pid,did,mid;
			while((line=br.readLine())!=null){
				String[] frags = line.split(" ");
				aid = Integer.parseInt(frags[1]);
				if(!dataSet.topASet.contains(aid)){continue;}
				pid = Integer.parseInt(frags[2]);
				m_id = aid * DataSet.P + pid - 1;
				if(!mMap.containsKey(m_id)){mMap.put(m_id, mMap.size()+1);}
				mid = mMap.get(m_id);
				
				uid = Integer.parseInt(frags[0]);
				did = Integer.parseInt(frags[3]);
				bw.write(uid+" "+mid+" "+did+"\n");
			}
			br.close();
			bw.close();
			Tools.saveMap(mMap, getFile(fsuf_mMap));
			
			//Write Record for regression
			HashSet<Record> records = new HashSet<Record>();
			br = new BufferedReader(new FileReader(fin_record));
			while((line=br.readLine())!=null){
				String[] frags = line.split(" ");
				uid = Integer.parseInt(frags[0]);
				aid = Integer.parseInt(frags[1]);
				if(!dataSet.topASet.contains(aid)){continue;}
				pid = Integer.parseInt(frags[2]);
				m_id = aid * DataSet.P + pid - 1;
				if(mMap.containsKey(m_id)){
					mid = mMap.get(m_id);
					records.add(new Record(uid, mid));
				}
			}
			br.close();
			bw = new BufferedWriter(new FileWriter(getFile(fsuf_record)));
			for(Record record:records){
				bw.write(record.uid+" "+record.mid+"\n");
			}
			bw.close();
		}
		
		new File(getFile(fsuf_uf)).delete();
		new File(getFile(fsuf_mf)).delete();
		new File(getFile(fsuf_prediction)).delete();
		new File(getFile(fsuf_log)).delete();
	}	
	class Record{
		public Integer uid;
		public Integer mid;
		public Record(int uid, int mid){this.uid = uid;this.mid = mid;}
		@Override
		public boolean equals(Object obj) {
			Record tgt = (Record)obj;
			return uid.equals(tgt.uid) && mid.equals(tgt.mid);
		}
		@Override
		public int hashCode() {
			return uid * DataSet.P + mid-1;
		}	
	}
	
	public String genScript(String libFolder){
		return "R --slave --args "+N
				+" "+getFile(fsuf_matrix)+" "+getFile(fsuf_uf)+" "+getFile(fsuf_mf)
				+" "+getFile(fsuf_record)+" "+getFile(fsuf_prediction)+" "+getFile(fsuf_log)
				+" < "+libFolder+"rscript/irlba.R";
	}
	
	
	public String genTestScript(String libFolder, int flag, String fin_train, String fin_test) throws Exception{
		mMap = Tools.loadMap(getFile(fsuf_mMap));
		//Read Predictions
		HashMap<Long, Double> predictions = new HashMap<Long, Double>();
		BufferedReader br_record = new BufferedReader(new FileReader(getFile(fsuf_record)));
		BufferedReader br_prediction = new BufferedReader(new FileReader(getFile(fsuf_prediction)));
		String line; int uid, mid;double prediction;
		while((line=br_record.readLine())!=null){
			String[] frags = line.split(" ");
			uid = Integer.parseInt(frags[0]);
			mid = Integer.parseInt(frags[1]);
			line = br_prediction.readLine();
			prediction = Double.parseDouble(line);
			predictions.put((long)uid * mMap.size() + mid - 1, prediction);
		}
		br_record.close();
		br_prediction.close();
		
		//Generate New LibLinear input
		BufferedReader br = new BufferedReader(new FileReader(fin_train));
		BufferedWriter bw = new BufferedWriter(new FileWriter(getFile(fsuf_lltrain)));
		int aid,pid;long m_id;
		while((line=br.readLine())!=null){
			String[] frags = line.split(" ");
			uid = Integer.parseInt(frags[0]);
			aid = Integer.parseInt(frags[1]);
			pid = Integer.parseInt(frags[2]);
			
			m_id = (flag == FLAG_UP)?pid:(aid*DataSet.P + pid -1);
			if(mMap.containsKey(m_id)){
				long e_id = (long)uid * mMap.size() + mMap.get(m_id) - 1 ;
				if(predictions.containsKey(e_id)){
					bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" "+predictions.get(e_id)+" "+frags[3]+"\n");
				}else{
					bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" 0 "+frags[3]+"\n");
				}
			}else{
				bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" 0 "+frags[3]+"\n");
			}
		}
		br.close();
		bw.close();
		br = new BufferedReader(new FileReader(fin_test));
		bw = new BufferedWriter(new FileWriter(getFile(fsuf_lltest)));
		while((line=br.readLine())!=null){
			String[] frags = line.split(" ");
			uid = Integer.parseInt(frags[0]);
			aid = Integer.parseInt(frags[1]);
			pid = Integer.parseInt(frags[2]);
			
			m_id = (flag == FLAG_UP)?pid:(aid*DataSet.P + pid -1);
			if(mMap.containsKey(m_id)){
				long e_id = (long)uid * mMap.size() + mMap.get(m_id) - 1 ;
				if(predictions.containsKey(e_id)){
					bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" "+predictions.get(e_id)+" "+frags[3]+"\n");
				}else{
					bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" 0 "+frags[3]+"\n");
				}
			}else{
				bw.write(frags[0]+" "+frags[1]+" "+frags[2]+" 0 "+frags[3]+"\n");
			}
		}
		br.close();
		bw.close();
		
		//Generate LibLinear Script
		LibLinear ll = new LibLinear(folder, filePrefix+"_liblinear");
		int[] flags = new int[]{LibLinear.FLAG_NOMINAL,LibLinear.FLAG_NOMINAL,LibLinear.FLAG_NOMINAL, LibLinear.FLAG_NUMERIC, LibLinear.FLAG_NUMERIC};
		boolean[] valids = new boolean[]{true,true,true,true,true};
		ll.genData(getFile(fsuf_lltrain), getFile(fsuf_lltest), flags, valids);
		
		return ll.genScript(libFolder);
	}
	
	public double getTestAccuracy() throws Exception{
		LibLinear ll = new LibLinear(folder, filePrefix+"_liblinear");
		return ll.getAccuracy();
	}

}
